001 /* 002 * Copyright 2005 Stephen J. McConnell 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.library.impl; 020 021 import java.io.File; 022 import java.io.IOException; 023 import java.net.URI; 024 import java.net.URISyntaxException; 025 import java.text.SimpleDateFormat; 026 import java.util.Arrays; 027 import java.util.ArrayList; 028 import java.util.List; 029 import java.util.Date; 030 import java.util.TimeZone; 031 import java.util.Properties; 032 import java.util.Map; 033 import java.util.Hashtable; 034 035 import net.dpml.lang.Category; 036 import net.dpml.lang.Version; 037 038 import net.dpml.library.Info; 039 import net.dpml.library.Filter; 040 import net.dpml.library.Library; 041 import net.dpml.library.Module; 042 import net.dpml.library.Resource; 043 import net.dpml.library.Type; 044 import net.dpml.library.Data; 045 import net.dpml.library.ResourceNotFoundException; 046 import net.dpml.library.info.InfoDirective; 047 import net.dpml.library.info.TypeDirective; 048 import net.dpml.library.info.ResourceDirective; 049 import net.dpml.library.info.ResourceDirective.Classifier; 050 import net.dpml.library.info.IncludeDirective; 051 import net.dpml.library.info.DependencyDirective; 052 import net.dpml.library.info.AbstractDirective; 053 import net.dpml.library.info.ValidationException; 054 import net.dpml.library.info.FilterDirective; 055 import net.dpml.library.info.Scope; 056 057 import net.dpml.transit.Artifact; 058 import net.dpml.transit.Transit; 059 060 import net.dpml.util.Resolver; 061 062 063 /** 064 * Implementation of a resource. 065 * 066 * @author <a href="http://www.dpml.net">The Digital Product Meta Library</a> 067 * @version 1.0.0 068 */ 069 public class DefaultResource extends DefaultDictionary implements Resource, Resolver, Comparable 070 { 071 /** 072 * Timestamp. 073 */ 074 public static final String TIMESTAMP = getTimestamp(); 075 076 /** 077 * Constant SNAPSHOT symbol. 078 */ 079 public static final String SNAPSHOT = "SNAPSHOT"; 080 081 /** 082 * Constant RELEASE symbol. 083 */ 084 public static final String RELEASE = "RELEASE"; 085 086 private final DefaultLibrary m_library; 087 private final ResourceDirective m_directive; 088 private final DefaultModule m_parent; 089 private final Type[] m_types; 090 private final String[] m_typeNames; 091 private final String m_path; 092 private final File m_basedir; 093 private final Map m_filters = new Hashtable(); 094 private final Data[] m_data; 095 096 /** 097 * Creation of a new default resource. 098 * @param logger the assigned logging channel 099 * @param library the reference library 100 * @param directive the directive 101 */ 102 DefaultResource( final DefaultLibrary library, final AbstractDirective directive ) 103 { 104 super( null, directive ); 105 106 m_library = library; 107 m_directive = null; 108 m_parent = null; 109 m_types = new Type[0]; 110 m_typeNames = new String[0]; 111 m_path = ""; 112 m_basedir = null; 113 m_data = new Data[0]; 114 } 115 116 /** 117 * Creation of a new default resource. 118 * @param logger the assigned logging channel 119 * @param library the reference library 120 * @param module the parent module 121 * @param directive the resource directive 122 */ 123 DefaultResource( 124 final DefaultLibrary library, final DefaultModule module, final ResourceDirective directive ) 125 { 126 super( module, directive ); 127 if( null == directive ) 128 { 129 throw new NullPointerException( "directive" ); 130 } 131 132 m_library = library; 133 m_directive = directive; 134 m_parent = module; 135 136 if( module.isRoot() ) 137 { 138 m_path = directive.getName(); 139 } 140 else 141 { 142 m_path = module.getResourcePath() + "/" + directive.getName(); 143 } 144 145 // setup produced types 146 147 //m_types = buildTypes( directive.getTypeDirectives() ); 148 m_types = directive.getTypeDirectives(); 149 m_typeNames = new String[ m_types.length ]; 150 for( int i=0; i<m_types.length; i++ ) 151 { 152 Type type = m_types[i]; 153 m_typeNames[i] = type.getID(); 154 } 155 156 // setup production data 157 158 m_data = new Data[0]; 159 160 // setup the resource basedir 161 162 File anchor = getAnchor(); 163 String filename = m_directive.getBasedir(); 164 if( null != filename ) 165 { 166 String spec = resolve( filename ); 167 File file = new File( spec ); 168 if( file.isAbsolute() ) 169 { 170 m_basedir = getCanonicalFile( file ); 171 } 172 else 173 { 174 File basedir = new File( anchor, spec ); 175 m_basedir = getCanonicalFile( basedir ); 176 setProperty( "basedir", m_basedir.toString() ); 177 } 178 } 179 else 180 { 181 if( !m_directive.getClassifier().equals( Classifier.LOCAL ) ) 182 { 183 m_basedir = null; 184 } 185 else 186 { 187 final String error = 188 "Missing base directory declaration in resource [" 189 + m_path 190 + "]."; 191 throw new ValidationException( error ); 192 } 193 } 194 195 // setup the default properties 196 197 setProperty( "project.name", getName() ); 198 if( null != m_parent ) 199 { 200 setProperty( "project.group", m_parent.getResourcePath() ); 201 } 202 else 203 { 204 setProperty( "project.group", "" ); 205 } 206 String version = getVersion(); 207 if( null != version ) 208 { 209 setProperty( "project.version", getVersion() ); 210 } 211 212 FilterDirective[] filters = directive.getFilterDirectives(); 213 for( int i=0; i<filters.length; i++ ) 214 { 215 FilterDirective filter = filters[i]; 216 String token = filter.getToken(); 217 m_filters.put( token, filter ); 218 } 219 } 220 221 //---------------------------------------------------------------------------- 222 // Resource 223 //---------------------------------------------------------------------------- 224 225 /** 226 * Return a data directives. 227 * @return the associated production data 228 */ 229 public Data[] getData() 230 { 231 return m_data; 232 } 233 234 /** 235 * Return the singleton library. 236 * @return the library 237 */ 238 public Library getLibrary() 239 { 240 return m_library; 241 } 242 243 /** 244 * Return the name of the resource. 245 * @return the resource name 246 */ 247 public String getName() 248 { 249 if( null != m_directive ) 250 { 251 return m_directive.getName(); 252 } 253 else 254 { 255 return null; 256 } 257 } 258 259 /** 260 * Return the resource version. 261 * @return the version 262 */ 263 public String getVersion() 264 { 265 if( null == m_directive ) 266 { 267 return getStandardVersion(); 268 } 269 if( ResourceDirective.ANONYMOUS.equals( getClassifier() ) ) 270 { 271 return m_directive.getVersion(); 272 } 273 else 274 { 275 String version = getStatutoryVersion(); 276 if( null != version ) 277 { 278 return version; 279 } 280 else 281 { 282 return getStandardVersion(); 283 } 284 } 285 } 286 287 /** 288 * Return the declard resource version. 289 * @return the statutory version 290 */ 291 public String getStatutoryVersion() 292 { 293 if( null == m_directive ) 294 { 295 return null; 296 } 297 else 298 { 299 String version = m_directive.getVersion(); 300 if( null != version ) 301 { 302 return version; 303 } 304 else 305 { 306 if( null != m_parent ) 307 { 308 return m_parent.getStatutoryVersion(); 309 } 310 else 311 { 312 return null; 313 } 314 } 315 } 316 } 317 318 /** 319 * Return the decimal version. If version prefixing is enabled 320 * via the <tt>project.version-prefix.enabled</tt> property then the value 321 * returned will be derived from the project major, minor and micro version 322 * properties, otherwise a null value will be returned. 323 * 324 * @return the version 325 */ 326 public Version getDecimalVersion() 327 { 328 boolean flag = getBooleanProperty( "project.version-prefix.enabled", false ); 329 if( flag ) 330 { 331 int major = getMajorVersion(); 332 int minor = getMinorVersion(); 333 int micro = getMicroVersion(); 334 return new Version( major, minor, micro ); 335 } 336 else 337 { 338 return null; 339 } 340 } 341 342 /** 343 * Return the fully qualified path to the resource. 344 * @return the path 345 */ 346 public String getResourcePath() 347 { 348 return m_path; 349 } 350 351 /** 352 * Return the basedir for this resource. 353 * @return the base directory (possibly null) 354 */ 355 public File getBaseDir() 356 { 357 return m_basedir; 358 } 359 360 /** 361 * Return the resource classifier. 362 * @return the classifier (LOCAL, EXTERNAL or ANONYMOUS) 363 */ 364 public Classifier getClassifier() 365 { 366 if( null != m_directive ) 367 { 368 return m_directive.getClassifier(); 369 } 370 else 371 { 372 return ResourceDirective.ANONYMOUS; 373 } 374 } 375 376 /** 377 * Return the info block. 378 * @return the info block 379 */ 380 public Info getInfo() 381 { 382 return m_directive.getInfoDirective(); 383 } 384 385 /** 386 * Return the expanded array of types associated with the resource. 387 * The returned array is a function of the types declared by a resource 388 * expanded relative to any types implied by processor dependencies. 389 * @return the type array 390 */ 391 public Type[] getTypes() 392 { 393 return m_types; 394 } 395 396 /** 397 * Test if this resource is associated with a type of the supplied name. 398 * @param type the type id 399 * @return TRUE if this resource produces an artifact of the supplied type 400 */ 401 public boolean isa( final String type ) 402 { 403 for( int i=0; i<m_types.length; i++ ) 404 { 405 Type someType = m_types[i]; 406 String name = someType.getID(); 407 if( name.equals( type ) ) 408 { 409 return true; 410 } 411 } 412 return false; 413 } 414 415 /** 416 * Return a resource type relative to a supplied type id. 417 * @param id the type name to retrieve 418 * @return the type instance 419 * @exception IllegalArgumentException if the id value does not match 420 * a type produced by the resource. 421 */ 422 public Type getType( final String id ) throws IllegalArgumentException 423 { 424 for( int i=0; i<m_types.length; i++ ) 425 { 426 Type type = m_types[i]; 427 if( type.getID().equals( id ) ) 428 { 429 return type; 430 } 431 } 432 final String error = 433 "Type name [" 434 + id 435 + "] not recognized with the scope of resource [" 436 + getResourcePath() 437 + "]."; 438 throw new IllegalArgumentException( error ); 439 } 440 441 /** 442 * Construct an link artifact for the supplied type. 443 * @param id the resource type id 444 * @return the link artifact 445 */ 446 public Artifact getLinkArtifact( final String id ) 447 { 448 if( null == m_directive ) 449 { 450 final String error = 451 "Method not supported on virtual root."; 452 throw new UnsupportedOperationException( error ); 453 } 454 if( null == id ) 455 { 456 throw new NullPointerException( "id" ); 457 } 458 String group = getGroupName(); 459 String name = getName(); 460 Type type = getType( id ); 461 Version version = type.getVersion(); 462 if( null == version ) 463 { 464 final String error = 465 "Resource does not declare production of an alias for the requested type." 466 + "\nResource: " + this 467 + "\nType: " + id; 468 throw new IllegalArgumentException( error ); 469 } 470 try 471 { 472 String spec = "link:" + id; 473 if( null != group ) 474 { 475 spec = spec + ":" + group + "/" + name; 476 } 477 else 478 { 479 spec = spec + ":" + name; 480 } 481 if( !Version.NULL_VERSION.equals( version ) ) 482 { 483 int major = version.getMajor(); 484 int minor = version.getMinor(); 485 spec = spec + "#" 486 + major 487 + "." 488 + minor; 489 } 490 return Artifact.createArtifact( spec ); 491 } 492 catch( Throwable e ) 493 { 494 final String error = 495 "Failed to construct link artifact for resource [" 496 + getResourcePath() 497 + "]."; 498 throw new RuntimeException( error, e ); 499 } 500 } 501 502 /** 503 * Construct an artifact for the supplied type. 504 * @param id the resource type identifier 505 * @return the artifact 506 */ 507 public Artifact getArtifact( final String id ) 508 { 509 if( null == m_directive ) 510 { 511 final String error = 512 "Method not supported on virtual root."; 513 throw new UnsupportedOperationException( error ); 514 } 515 if( null == id ) 516 { 517 throw new NullPointerException( "id" ); 518 } 519 520 String group = getGroupName(); 521 String name = getName(); 522 String version = getVersion(); 523 String scheme = m_directive.getScheme(); 524 525 try 526 { 527 return Artifact.createArtifact( scheme, group, name, version, id ); 528 } 529 catch( Throwable e ) 530 { 531 final String error = 532 "Failed to construct artifact for resource [" 533 + getResourcePath() 534 + "]."; 535 throw new RuntimeException( error, e ); 536 } 537 } 538 539 /** 540 * Return the enclosing parent module. 541 * @return the enclosing module of null if this a top-level module. 542 */ 543 public Module getParent() 544 { 545 return getDefaultParent(); 546 } 547 548 /** 549 * Return an array of filters associated with the resource. 550 * @return the array of filters 551 */ 552 public Filter[] getFilters() 553 { 554 DefaultModule module = getDefaultParent(); 555 if( null != module ) 556 { 557 Map map = new Hashtable(); 558 Filter[] filters = module.getFilters(); 559 for( int i=0; i<filters.length; i++ ) 560 { 561 Filter filter = filters[i]; 562 String token = filter.getToken(); 563 map.put( token, filter ); 564 } 565 Filter[] local = getLocalFilters(); 566 for( int i=0; i<local.length; i++ ) 567 { 568 Filter filter = local[i]; 569 String token = filter.getToken(); 570 map.put( token, filter ); 571 } 572 return (Filter[]) map.values().toArray( new Filter[0] ); 573 } 574 else 575 { 576 return getLocalFilters(); 577 } 578 } 579 580 //---------------------------------------------------------------------------- 581 // Resolver 582 //---------------------------------------------------------------------------- 583 584 /** 585 * Utility function supporting resolution of uris containing 'resource' or 586 * 'alias' schemes. If the supplied uri schem is 'resource' or 'alias' the 587 * reference is resolved to a artifact type, group and name from which a 588 * resource is resolved and the uri returned. If the scheme is resource 589 * the usri of the resource is returned. If the scheme is 'alias' a 590 * linkn alias is returned. If the scheme is not 'resource' or 'alias' 591 * the argument will be evaluated as a normal transit artifact uri 592 * specification. 593 * 594 * @param ref the uri argument 595 * @return the uri value 596 * @exception URISyntaxException if an error occurs during uri creation 597 */ 598 public URI toURI( final String ref ) throws URISyntaxException 599 { 600 Artifact spec = Artifact.createArtifact( ref ); 601 if( spec.isRecognized() ) 602 { 603 return spec.toURI(); 604 } 605 else if( ref.startsWith( "resource:" ) || ref.startsWith( "alias:" ) ) 606 { 607 String type = spec.getType(); 608 String group = spec.getGroup(); 609 String name = spec.getName(); 610 String path = group + "/" + name; 611 Library library = getLibrary(); 612 try 613 { 614 Resource resource = library.getResource( path ); 615 if( ref.startsWith( "resource:" ) ) 616 { 617 Artifact artifact = resource.getArtifact( type ); 618 return artifact.toURI(); 619 } 620 else 621 { 622 Artifact artifact = resource.getLinkArtifact( type ); 623 return artifact.toURI(); 624 } 625 } 626 catch( ResourceNotFoundException e ) 627 { 628 final String error = 629 "Unresolvable resource reference: " + path; 630 IllegalArgumentException iae = new IllegalArgumentException( error ); 631 iae.initCause( e ); 632 throw iae; 633 } 634 } 635 else 636 { 637 return spec.toURI(); 638 } 639 } 640 641 //---------------------------------------------------------------------------- 642 // implementation 643 //---------------------------------------------------------------------------- 644 645 Map getFilterMap() 646 { 647 return m_filters; 648 } 649 650 Filter[] getLocalFilters() 651 { 652 return (Filter[]) getFilterMap().values().toArray( new Filter[0] ); 653 } 654 655 /** 656 * Return an array of resource that are providers to this resource. 657 * @param scope the operational scope 658 * @param expand if true include transitive dependencies 659 * @param sort if true the array will sorted relative to dependencies 660 * @return the resource providers 661 */ 662 public Resource[] getProviders( final Scope scope, final boolean expand, final boolean sort ) 663 { 664 return getDefaultProviders( scope, expand, sort ); 665 } 666 667 /** 668 * Return an array of resource that are providers to this resource. If 669 * the supplied scope is BUILD the returned resource array is equivalent 670 * <src>getProviders( Scope.BUILD, .. )</src>. If the scope is RUNTIME 671 * the returned resource array includes BUILD and RUNTIME resources. If 672 * the scope is TEST the returned array includes BUILD, RUNTIME and TEST 673 * resources. 674 * @param scope the scope of aggregation to be applied to the selection 675 * @param expand if TRUE include transitive dependencies 676 * @param sort if true the array will sorted relative to dependencies 677 * @return the resource providers 678 */ 679 public Resource[] getAggregatedProviders( final Scope scope, final boolean expand, final boolean sort ) 680 { 681 return getAggregatedDefaultProviders( scope, expand, sort, false ); 682 } 683 684 /** 685 * Return a sorted and filtered array of providers. Resources not declaring 686 * the "jar" type as a produced type are excluded from selection. The 687 * resource array will include transitive dependencies. The method is 688 * suitable for the construction of build and test phase classloaders. 689 * 690 * @param scope the aggregation scope 691 * @return the scoped resource chain 692 */ 693 public Resource[] getClasspathProviders( final Scope scope ) 694 { 695 DefaultResource[] result = getAggregatedDefaultProviders( scope, true, true, true ); 696 List stack = new ArrayList(); 697 for( int i=0; i<result.length; i++ ) 698 { 699 DefaultResource resource = result[i]; 700 if( resource.isa( "jar" ) ) 701 { 702 stack.add( resource ); 703 } 704 } 705 return (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 706 } 707 708 /** 709 * Return an array of runtime providers filtered relative to a supplied 710 * classloading category. Resources not declaring the "jar" type as a 711 * produced type are excluded from selection. The resource array returned 712 * from this operation is a sorted transitive sequence excluding all 713 * resource references by any category higher than the supplied category. 714 * This method is typically used to construct information suitable for 715 * the gerneration of plugin metadata. 716 * 717 * @param category the classloader category 718 * @return the category scoped resource chain 719 */ 720 public Resource[] getClasspathProviders( final Category category ) 721 { 722 DefaultResource[] resources = getClasspathDefaultProviders( category ); 723 return sortDefaultResources( resources, Scope.RUNTIME ); 724 } 725 726 /** 727 * Return an array of resources that are consumers of this resource. 728 * @param expand if true the returned array includes consumers associated 729 * through transitive dependency relationships, otherwise the array is 730 * limited to direct consumers 731 * @param sort if true the array is sorted relative to depenency relationships 732 * @return the array of consumer projects 733 */ 734 public Resource[] getConsumers( final boolean expand, final boolean sort ) 735 { 736 return getDefaultConsumers( expand, sort ); 737 } 738 739 /** 740 * Return the underlying resource defintion. 741 * @return the resource directive 742 */ 743 public ResourceDirective getResourceDirective() 744 { 745 return m_directive; 746 } 747 748 /** 749 * Return a filename using the layout strategy employed by the cache. 750 * @param id the artifact type 751 * @return the filename 752 */ 753 public String getLayoutPath( final String id ) 754 { 755 Artifact artifact = getArtifact( id ); 756 return Transit.getInstance().getCacheLayout().resolveFilename( artifact ); 757 } 758 759 /** 760 * Return a directive suitable for publication as an external description. 761 * @param module the enclosing module 762 * @return the resource directive 763 */ 764 ResourceDirective exportResource( final DefaultModule module ) 765 { 766 if( null == m_directive ) 767 { 768 final String error = 769 "Cannot export from the root module."; 770 throw new UnsupportedOperationException( error ); 771 } 772 String name = getName(); 773 String version = getVersion(); 774 String basedir = null; 775 InfoDirective info = m_directive.getInfoDirective(); 776 TypeDirective[] types = m_directive.getTypeDirectives(); 777 TypeDirective[] exportedTypes = createExportedTypes( types ); 778 DependencyDirective[] dependencies = createDeps( module ); 779 Properties properties = getExportProperties(); 780 return ResourceDirective.createResourceDirective( 781 name, version, Classifier.EXTERNAL, basedir, 782 info, exportedTypes, dependencies, properties, null ); 783 } 784 785 TypeDirective[] createExportedTypes( final TypeDirective[] types ) 786 { 787 TypeDirective[] export = new TypeDirective[ types.length ]; 788 for( int i=0; i<export.length; i++ ) 789 { 790 TypeDirective type = types[i]; 791 String id = type.getID(); 792 Version version = type.getVersion(); 793 export[i] = new TypeDirective( id, version ); 794 } 795 return export; 796 } 797 798 private DependencyDirective[] createDeps( final DefaultModule module ) 799 { 800 ArrayList list = new ArrayList(); 801 createIncludeDirectives( module, list, Category.SYSTEM ); 802 createIncludeDirectives( module, list, Category.PUBLIC ); 803 createIncludeDirectives( module, list, Category.PROTECTED ); 804 createIncludeDirectives( module, list, Category.PRIVATE ); 805 if( list.size() == 0 ) 806 { 807 return new DependencyDirective[0]; 808 } 809 else 810 { 811 IncludeDirective[] includes = 812 (IncludeDirective[]) list.toArray( new IncludeDirective[0] ); 813 DependencyDirective runtime = 814 new DependencyDirective( Scope.RUNTIME, includes ); 815 return new DependencyDirective[]{runtime}; 816 } 817 } 818 819 boolean isaDescendant( final DefaultModule module ) 820 { 821 if( module == this ) 822 { 823 return true; 824 } 825 if( m_parent == null ) 826 { 827 return false; 828 } 829 else 830 { 831 if( m_parent == module ) 832 { 833 return true; 834 } 835 else 836 { 837 return m_parent.isaDescendant( module ); 838 } 839 } 840 } 841 842 private void createIncludeDirectives( 843 final DefaultModule module, final List list, final Category category ) 844 { 845 DefaultResource[] providers = 846 getDefaultProviders( Scope.RUNTIME, true, category ); 847 for( int i=0; i<providers.length; i++ ) 848 { 849 DefaultResource provider = providers[i]; 850 if( provider.isaDescendant( module ) ) 851 { 852 // create a ref 853 String path = provider.getResourcePath(); 854 IncludeDirective include = 855 new IncludeDirective( 856 IncludeDirective.REF, 857 category, 858 path, 859 null ); 860 list.add( include ); 861 } 862 else 863 { 864 // create a urn 865 866 Type[] types = provider.getTypes(); 867 for( int j=0; j<types.length; j++ ) 868 { 869 Type type = types[j]; 870 String label = type.getID(); 871 Artifact artifact = provider.getArtifact( label ); 872 String urn = artifact.toString(); 873 IncludeDirective include = 874 new IncludeDirective( 875 IncludeDirective.URI, 876 category, 877 urn, 878 null ); 879 list.add( include ); 880 } 881 } 882 } 883 } 884 885 //---------------------------------------------------------------------------- 886 // Object 887 //---------------------------------------------------------------------------- 888 889 /** 890 * Return a string representation of the resource in the form 'resource:[path]'. 891 * @return the string value 892 */ 893 public String toString() 894 { 895 if( null != m_directive ) 896 { 897 if( m_directive.isLocal() ) 898 { 899 return toString( "project" ); 900 } 901 } 902 return toString( "resource" ); 903 } 904 905 String toString( final String type ) 906 { 907 return type + ":" + getResourcePath() + "#" + getVersion(); 908 } 909 910 /** 911 * Compare this object with another. 912 * @param other the other object 913 * @return the comparitive index 914 */ 915 public int compareTo( final Object other ) 916 { 917 if( other instanceof DefaultResource ) 918 { 919 DefaultResource resource = (DefaultResource) other; 920 return getResourcePath().compareTo( resource.m_path ); 921 } 922 else 923 { 924 return -1; 925 } 926 } 927 928 //---------------------------------------------------------------------------- 929 // internals 930 //---------------------------------------------------------------------------- 931 932 /** 933 * Return the singlton library. 934 * @return the library 935 */ 936 DefaultLibrary getDefaultLibrary() 937 { 938 return m_library; 939 } 940 941 boolean isAnonymous() 942 { 943 if( null != m_directive ) 944 { 945 return m_directive.isAnonymous(); 946 } 947 return false; 948 } 949 950 boolean isLocal() 951 { 952 if( null != m_directive ) 953 { 954 return m_directive.isLocal(); 955 } 956 return false; 957 } 958 959 DefaultModule getDefaultParent() 960 { 961 if( null != m_parent ) 962 { 963 if( m_parent.isRoot() ) 964 { 965 return null; 966 } 967 } 968 return m_parent; 969 } 970 971 DefaultResource[] getAggregatedDefaultProviders( 972 final Scope scope, final boolean expanded, final boolean sort, final boolean flag ) 973 { 974 DefaultResource[] resources = 975 getAggregatedDefaultProviders( scope, expanded, flag ); 976 if( sort ) 977 { 978 return sortDefaultResources( resources, scope ); 979 } 980 else 981 { 982 Arrays.sort( resources ); 983 return resources; 984 } 985 } 986 987 DefaultResource[] getAggregatedDefaultProviders( 988 final Scope scope, final boolean expanded, final boolean flag ) 989 { 990 ArrayList list = new ArrayList(); 991 if( !flag ) 992 { 993 aggregateProviders( list, Scope.BUILD ); 994 } 995 if( scope.isGreaterThan( Scope.BUILD ) ) 996 { 997 aggregateProviders( list, Scope.RUNTIME ); 998 } 999 if( scope.isGreaterThan( Scope.RUNTIME ) ) 1000 { 1001 aggregateProviders( list, Scope.TEST ); 1002 } 1003 DefaultResource[] result = (DefaultResource[]) list.toArray( new DefaultResource[0] ); 1004 if( expanded ) 1005 { 1006 List visited = new ArrayList(); 1007 List stack = new ArrayList(); 1008 for( int i=0; i<result.length; i++ ) 1009 { 1010 DefaultResource resource = result[i]; 1011 expandDefaultResource( visited, stack, scope, resource ); 1012 } 1013 result = (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 1014 } 1015 return result; 1016 } 1017 1018 private void aggregateProviders( final List list, final Scope scope ) 1019 { 1020 DefaultResource[] resources = getDefaultProviders( scope, false, null ); 1021 for( int i=0; i<resources.length; i++ ) 1022 { 1023 DefaultResource resource = resources[i]; 1024 if( !list.contains( resource ) ) 1025 { 1026 list.add( resource ); 1027 } 1028 } 1029 } 1030 1031 DefaultResource[] getDefaultProviders( 1032 final Scope scope, final boolean expanded, final boolean sort ) 1033 { 1034 DefaultResource[] resources = getDefaultProviders( scope, expanded, null ); 1035 if( sort ) 1036 { 1037 return sortDefaultResources( resources, scope ); 1038 } 1039 else 1040 { 1041 Arrays.sort( resources ); 1042 return resources; 1043 } 1044 } 1045 1046 DefaultResource[] getDefaultProviders( 1047 final Scope scope, final boolean expand, final Category category ) 1048 { 1049 ArrayList visited = new ArrayList(); 1050 ArrayList stack = new ArrayList(); 1051 DefaultResource[] providers = getLocalDefaultProviders( scope, category ); 1052 for( int i=0; i<providers.length; i++ ) 1053 { 1054 DefaultResource provider = providers[i]; 1055 if( expand ) 1056 { 1057 expandDefaultResource( visited, stack, scope, provider ); 1058 } 1059 else if( !stack.contains( provider ) ) 1060 { 1061 stack.add( provider ); 1062 } 1063 } 1064 return (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 1065 } 1066 1067 DefaultResource[] getLocalDefaultProviders( final Scope scope, final Category category ) 1068 { 1069 if( null == m_directive ) 1070 { 1071 return new DefaultResource[0]; 1072 } 1073 IncludeDirective[] includes = getLocalIncludes( scope, category ); 1074 DefaultResource[] resources = new DefaultResource[ includes.length ]; 1075 for( int i=0; i<includes.length; i++ ) 1076 { 1077 IncludeDirective include = includes[i]; 1078 if( include.getMode().equals( IncludeDirective.URI ) ) 1079 { 1080 try 1081 { 1082 String value = include.getValue(); 1083 String urn = resolve( value ); 1084 Properties properties = include.getProperties(); 1085 resources[i] = m_library.getAnonymousResource( urn, properties ); 1086 } 1087 catch( URISyntaxException e ) 1088 { 1089 final String error = 1090 "Invalid uri value: " + include.getValue(); 1091 throw new RuntimeException( error, e ); 1092 } 1093 catch( InvalidNameException e ) 1094 { 1095 final String error = 1096 "An anonomous dependency include reference to [" 1097 + include 1098 + "] within the resource [" 1099 + getResourcePath() 1100 + "] could not be resolved."; 1101 throw new InvalidNameException( error, e ); 1102 } 1103 catch( Exception e ) 1104 { 1105 final String error = 1106 "Unexpected error during dynamic resource creation."; 1107 throw new RuntimeException( error, e ); 1108 } 1109 } 1110 else 1111 { 1112 String ref = getIncludeReference( include ); 1113 try 1114 { 1115 DefaultResource resource = m_library.getDefaultResource( ref ); 1116 resources[i] = resource; 1117 } 1118 catch( InvalidNameException e ) 1119 { 1120 if( null == category ) 1121 { 1122 final String error = 1123 "A dependency include [" 1124 + ref 1125 + "] within [" 1126 + this 1127 + "] referencing [" 1128 + ref 1129 + "] under the scope [" 1130 + scope 1131 + "] is unknown."; 1132 throw new InvalidNameException( error ); 1133 } 1134 else 1135 { 1136 final String error = 1137 "A dependency include within [" 1138 + this 1139 + "] referencing [" 1140 + ref 1141 + "] under the scope [" 1142 + scope 1143 + "] and category [" 1144 + category 1145 + "] is unknown."; 1146 throw new InvalidNameException( error ); 1147 } 1148 } 1149 } 1150 } 1151 return resources; 1152 } 1153 1154 private IncludeDirective[] getLocalIncludes( final Scope scope, final Category category ) 1155 { 1156 DependencyDirective dependency = m_directive.getDependencyDirective( scope ); 1157 if( null == category ) 1158 { 1159 return dependency.getIncludeDirectives(); 1160 } 1161 else 1162 { 1163 return dependency.getIncludeDirectives( category ); 1164 } 1165 } 1166 1167 private void expandDefaultResource( 1168 final List visited, final List stack, final Scope scope, final DefaultResource resource ) 1169 { 1170 if( visited.contains( resource ) ) 1171 { 1172 return; 1173 } 1174 else 1175 { 1176 visited.add( resource ); 1177 boolean flag = !scope.equals( Scope.BUILD ); 1178 DefaultResource[] providers = resource.getAggregatedDefaultProviders( scope, false, flag ); 1179 for( int i=0; i<providers.length; i++ ) 1180 { 1181 DefaultResource provider = providers[i]; 1182 expandDefaultResource( visited, stack, scope, provider ); 1183 } 1184 stack.add( resource ); 1185 } 1186 } 1187 1188 private String getIncludeReference( final IncludeDirective directive ) 1189 { 1190 if( null == m_parent ) 1191 { 1192 return directive.getValue(); 1193 } 1194 else 1195 { 1196 if( IncludeDirective.REF.equals( directive.getMode() ) ) 1197 { 1198 return directive.getValue(); 1199 } 1200 else 1201 { 1202 String path = m_parent.getResourcePath(); 1203 if( "".equals( path ) ) 1204 { 1205 return directive.getValue(); 1206 } 1207 else 1208 { 1209 String key = directive.getValue(); 1210 return path + "/" + key; 1211 } 1212 } 1213 } 1214 } 1215 1216 //---------------------------------------------------------------------------- 1217 // consumer concerns 1218 //---------------------------------------------------------------------------- 1219 1220 boolean isaConsumer( final DefaultResource resource ) 1221 { 1222 DefaultResource[] resources = getAggregatedDefaultProviders( Scope.TEST, false, false ); 1223 for( int i=0; i<resources.length; i++ ) 1224 { 1225 DefaultResource provider = resources[i]; 1226 if( resource.equals( provider ) ) 1227 { 1228 return true; 1229 } 1230 } 1231 return false; 1232 } 1233 1234 DefaultResource[] getDefaultConsumers( final boolean expand, final boolean sort ) 1235 { 1236 DefaultResource[] consumers = getDefaultConsumers( expand ); 1237 if( sort ) 1238 { 1239 return sortDefaultResources( consumers, Scope.TEST ); 1240 } 1241 else 1242 { 1243 return consumers; 1244 } 1245 } 1246 1247 DefaultResource[] getDefaultConsumers( final boolean expand ) 1248 { 1249 if( !expand ) 1250 { 1251 ArrayList list = new ArrayList(); 1252 DefaultResource[] resources = m_library.selectDefaultResources( "**/*" ); 1253 for( int i=0; i<resources.length; i++ ) 1254 { 1255 DefaultResource resource = resources[i]; 1256 if( !list.contains( resource ) && resource.isaConsumer( this ) ) 1257 { 1258 list.add( resource ); 1259 } 1260 } 1261 return (DefaultResource[]) list.toArray( new DefaultResource[0] ); 1262 } 1263 else 1264 { 1265 ArrayList visited = new ArrayList(); 1266 ArrayList stack = new ArrayList(); 1267 DefaultResource[] consumers = getDefaultConsumers( false ); 1268 for( int i=0; i<consumers.length; i++ ) 1269 { 1270 DefaultResource consumer = consumers[i]; 1271 processConsumer( visited, stack, consumer ); 1272 } 1273 return (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 1274 } 1275 } 1276 1277 void processConsumer( final List visited, final List stack, final DefaultResource consumer ) 1278 { 1279 if( visited.contains( consumer ) ) 1280 { 1281 return; 1282 } 1283 visited.add( consumer ); 1284 stack.add( consumer ); 1285 DefaultResource[] resources = consumer.getDefaultConsumers( false, false ); 1286 for( int i=0; i<resources.length; i++ ) 1287 { 1288 DefaultResource resource = resources[i]; 1289 processConsumer( visited, stack, resource ); 1290 } 1291 } 1292 1293 //---------------------------------------------------------------------------- 1294 // classpath stuff 1295 //---------------------------------------------------------------------------- 1296 1297 /** 1298 * Construct an array of resources based on the RUNTIME scoped dependencies 1299 * associated with the supplied category. The implementation builds a list 1300 * of all preceeding categories as a basis for filtering the returned list ensuring 1301 * no duplicate references are returned. 1302 * @param category the runtime classloader category 1303 * @return the array of resources the define a classloader for the category 1304 */ 1305 private DefaultResource[] getClasspathDefaultProviders( final Category category ) 1306 { 1307 ArrayList list = new ArrayList(); 1308 for( int i=0; i<category.getValue(); i++ ) 1309 { 1310 Category c = Category.parse( i ); 1311 DefaultResource[] collection = 1312 getDefaultProviders( Scope.RUNTIME, true, c ); 1313 for( int j=0; j<collection.length; j++ ) 1314 { 1315 list.add( collection[j] ); 1316 } 1317 } 1318 DefaultResource[] resources = 1319 getDefaultProviders( Scope.RUNTIME, true, category ); 1320 ArrayList stack = new ArrayList(); 1321 for( int i=0; i<resources.length; i++ ) 1322 { 1323 DefaultResource resource = resources[i]; 1324 if( resource.isa( "jar" ) && !list.contains( resource ) ) 1325 { 1326 stack.add( resource ); 1327 } 1328 } 1329 1330 return (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 1331 } 1332 1333 //---------------------------------------------------------------------------- 1334 // sorting relative to dependencies 1335 //---------------------------------------------------------------------------- 1336 1337 DefaultResource[] sortDefaultResources( final DefaultResource[] resources ) 1338 { 1339 return sortDefaultResources( resources, Scope.TEST ); 1340 } 1341 1342 DefaultResource[] sortDefaultResources( final DefaultResource[] resources, final Scope scope ) 1343 { 1344 ArrayList visited = new ArrayList(); 1345 ArrayList stack = new ArrayList(); 1346 for( int i=0; i<resources.length; i++ ) 1347 { 1348 DefaultResource resource = resources[i]; 1349 resource.sortDefaultResource( visited, stack, scope, resources ); 1350 } 1351 return (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 1352 } 1353 1354 void sortDefaultResource( 1355 final List visited, final List stack, final Scope scope, final DefaultResource[] resources ) 1356 { 1357 if( visited.contains( this ) ) 1358 { 1359 return; 1360 } 1361 else 1362 { 1363 visited.add( this ); 1364 DefaultResource[] providers = 1365 getAggregatedDefaultProviders( scope, false, false ); 1366 for( int i=0; i<providers.length; i++ ) 1367 { 1368 DefaultResource provider = providers[i]; 1369 if( isaMember( resources, provider ) ) 1370 { 1371 provider.sortDefaultResource( visited, stack, scope, resources ); 1372 } 1373 } 1374 if( !stack.contains( this ) ) 1375 { 1376 stack.add( this ); 1377 } 1378 } 1379 } 1380 1381 boolean isaMember( final DefaultResource[] resources, final DefaultResource resource ) 1382 { 1383 for( int i=0; i<resources.length; i++ ) 1384 { 1385 DefaultResource r = resources[i]; 1386 if( resource == r ) 1387 { 1388 return true; 1389 } 1390 } 1391 return false; 1392 } 1393 1394 //---------------------------------------------------------------------------- 1395 // version utilities 1396 //---------------------------------------------------------------------------- 1397 1398 private String getStandardVersion() 1399 { 1400 String value = getBuildSignature(); 1401 if( value.equals( SNAPSHOT ) ) 1402 { 1403 return value; 1404 } 1405 else 1406 { 1407 Version decimal = getDecimalVersion(); 1408 if( null != decimal ) 1409 { 1410 boolean flag = getBooleanProperty( "project.version-postfix.enabled", true ); 1411 if( flag ) 1412 { 1413 return decimal.toString() + "-" + value; 1414 } 1415 else 1416 { 1417 return decimal.toString(); 1418 } 1419 } 1420 else 1421 { 1422 return value; 1423 } 1424 } 1425 } 1426 1427 private String getBuildSignature() 1428 { 1429 String system = System.getProperty( "build.signature", null ); 1430 String value = getProperty( "build.signature", system ); 1431 if( null == value ) 1432 { 1433 return SNAPSHOT; 1434 } 1435 else if( value.equals( "project.timestamp" ) ) 1436 { 1437 return TIMESTAMP; 1438 } 1439 else 1440 { 1441 return value; 1442 } 1443 } 1444 1445 private int getMajorVersion() 1446 { 1447 return getIntegerProperty( "project.major.version", 0 ); 1448 } 1449 1450 private int getMinorVersion() 1451 { 1452 return getIntegerProperty( "project.minor.version", 0 ); 1453 } 1454 1455 private int getMicroVersion() 1456 { 1457 return getIntegerProperty( "project.micro.version", 0 ); 1458 } 1459 1460 /** 1461 * Return the UTC YYMMDD.HHMMSSS signature of a date. 1462 * @return the UTC date-stamp signature 1463 */ 1464 public static String getTimestamp() 1465 { 1466 return getTimestamp( new Date() ); 1467 } 1468 1469 /** 1470 * Return the UTC YYMMDD.HHMMSSS signature of a date. 1471 * @param date the date 1472 * @return the UTC date-stamp signature 1473 */ 1474 public static String getTimestamp( final Date date ) 1475 { 1476 final SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd.HHmmss" ); 1477 sdf.setTimeZone( TimeZone.getTimeZone( "UTC" ) ); 1478 return sdf.format( date ); 1479 } 1480 1481 //---------------------------------------------------------------------------- 1482 // other utilities 1483 //---------------------------------------------------------------------------- 1484 1485 private File getAnchor() 1486 { 1487 if( null != m_parent ) 1488 { 1489 File anchor = m_parent.getBaseDir(); 1490 if( null != anchor ) 1491 { 1492 return anchor; 1493 } 1494 } 1495 return m_library.getRootDirectory(); 1496 } 1497 1498 File getCanonicalFile( final File file ) 1499 { 1500 try 1501 { 1502 return file.getCanonicalFile(); 1503 } 1504 catch( IOException e ) 1505 { 1506 final String error = 1507 "internal error while attempting to convert the file [" 1508 + file 1509 + "] to its canonical representation."; 1510 throw new RuntimeException( error, e ); 1511 } 1512 } 1513 1514 private String getGroupName() 1515 { 1516 if( m_parent.isRoot() ) 1517 { 1518 return null; 1519 } 1520 else 1521 { 1522 return m_parent.getResourcePath(); 1523 } 1524 } 1525 }